package com.yeskery.nut.util;

import com.yeskery.nut.bean.ApplicationContext;
import com.yeskery.nut.bean.BaseApplicationContext;
import com.yeskery.nut.bean.BeanException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.*;

/**
 * Bean工具类
 * @author sprout
 * @version 1.0
 * 2022-07-18 22:50
 */
public class BeanUtils {

    /** 忽略的包名 */
    private static final String[] IGNORE_PACKAGES = {"java", "javax"};

    /** 内部类或lambda表达式标志符 */
    private static final String INNER_CLASS_SYMBOL = "$";

    /** cglib代理类标志符 */
    private static final String CGLIB_CLASS_SYMBOL = "$$EnhancerByCGLIB$$";

    /**
     * 私有化构造方法
     */
    private BeanUtils() {
    }

    /**
     * 获取依赖的bean对象
     * @param applicationContext 应用上下文
     * @param clazz bean的原始class对象
     * @param beanName bean名称
     * @param genericType 原始类型
     * @return 依赖的bean对象
     */
    public static Object getDependBeanObject(ApplicationContext applicationContext, Class<?> clazz, String beanName, Type genericType) {
        if (genericType instanceof TypeVariable) {
            TypeVariable<?> typeVariable = (TypeVariable<?>) genericType;
            TypeVariable<?>[] typeParameters = typeVariable.getGenericDeclaration().getTypeParameters();
            int index = -1;
            for (int i = 0; i < typeParameters.length; i++) {
                if (typeVariable.getName().equals(typeParameters[i].getName())) {
                    index = i;
                    break;
                }
            }
            if (index == -1) {
                throw new BeanException("The Generic Class Info Not Found.");
            }
            Type genericSuperclass = clazz.getGenericSuperclass();
            if (isCglibProxyObject(clazz) && (genericSuperclass instanceof Class)) {
                genericSuperclass = ((Class<?>) genericSuperclass).getGenericSuperclass();
            }
            if (genericSuperclass instanceof ParameterizedType) {
                return getDependBeanObject(applicationContext, beanName, ((ParameterizedType) genericSuperclass).getActualTypeArguments()[index]);
            } else {
                return getDependBeanObject(applicationContext, beanName, genericType);
            }
        } else {
            return getDependBeanObject(applicationContext, beanName, genericType);
        }
    }

    /**
     * 获取依赖的bean对象
     * @param applicationContext 应用上下文
     * @param beanName bean名称
     * @param genericType 原始类型
     * @return 依赖的bean对象
     */
    public static Object getDependBeanObject(ApplicationContext applicationContext, String beanName, Type genericType) {
        Class<?> type;
        ParameterizedType parameterizedType = null;
        if (genericType instanceof ParameterizedType) {
            parameterizedType = (ParameterizedType) genericType;
            type = (Class<?>) parameterizedType.getRawType();
        } else {
            type = (Class<?>) genericType;
        }

        Object value;
        if (isArrayDependBean(type)) {
            Class<?> arrayType = getArrayComponentType(type);
            Collection<?> beans = StringUtils.isEmpty(beanName) ? applicationContext.getBeans(arrayType)
                    : Collections.singletonList(applicationContext.getBean(beanName, arrayType));
            value = beans.toArray((Object[]) Array.newInstance(arrayType, 0));
            return value;
        }
        if (type.isAssignableFrom(Collection.class) && parameterizedType != null) {
            Class<?> parameterType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
            value = StringUtils.isEmpty(beanName) ? applicationContext.getBeans(parameterType)
                    : Collections.singleton(applicationContext.getBean(beanName, parameterType));
        } else if (type.isAssignableFrom(List.class) && parameterizedType != null) {
            Class<?> parameterType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
            value = StringUtils.isEmpty(beanName) ? new ArrayList<>(applicationContext.getBeans(parameterType))
                    : Collections.singletonList(applicationContext.getBean(beanName, parameterType));
        } else if (type.isAssignableFrom(Set.class) && parameterizedType != null) {
            Class<?> parameterType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
            value = StringUtils.isEmpty(beanName) ? new HashSet<>(applicationContext.getBeans(parameterType))
                    : Collections.singleton(applicationContext.getBean(beanName, parameterType));
        } else if (type.isAssignableFrom(Map.class) && parameterizedType != null) {
            Class<?> parameterNameType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
            Class<?> parameterValueType = (Class<?>) parameterizedType.getActualTypeArguments()[1];
            if (!String.class.equals(parameterNameType)) {
                throw new BeanException("Only Support Map<String, ?> Format.");
            }
            Collection<?> beans = applicationContext.getBeans(parameterValueType);
            if (beans.isEmpty()) {
                value = Collections.emptyMap();
            } else {
                Map<String, Object> beanMap = new HashMap<>(beans.size());
                BaseApplicationContext baseApplicationContext = (BaseApplicationContext) applicationContext;
                baseApplicationContext.getSingletonBeans().entrySet().stream()
                        .filter(e -> beans.stream().anyMatch(r -> r == e.getValue())).forEach(e -> beanMap.put(e.getKey(), e.getValue()));
                baseApplicationContext.getPrototypeBeanDefinitions().entrySet().stream()
                        .filter(e -> e.getValue().getBeanClass().isAssignableFrom(parameterValueType))
                        .forEach(e -> beans.stream().filter(r -> e.getValue().getBeanClass().equals(r.getClass()))
                                .findFirst().ifPresent(r -> beanMap.put(e.getKey(), r)));
                value = beanMap;
            }
        } else {
            value = StringUtils.isEmpty(beanName) ? applicationContext.getBean(type)
                    : applicationContext.getBean(beanName, type);
        }
        return value;
    }

    /**
     * 获取依赖的bean对象类型
     * @param clazz bean的原始class对象
     * @param genericType 原始类型
     * @return 依赖的bean对象类型
     */
    public static Class<?> getDependBeanClass(Class<?> clazz, Type genericType) {
        if (genericType instanceof TypeVariable) {
            TypeVariable<?> typeVariable = (TypeVariable<?>) genericType;
            TypeVariable<?>[] typeParameters = typeVariable.getGenericDeclaration().getTypeParameters();
            int index = -1;
            for (int i = 0; i < typeParameters.length; i++) {
                if (typeVariable.getName().equals(typeParameters[i].getName())) {
                    index = i;
                    break;
                }
            }
            if (index == -1) {
                throw new BeanException("The Generic Class Info Not Found.");
            }
            Type genericSuperclass = clazz.getGenericSuperclass();
            if (genericSuperclass instanceof ParameterizedType) {
                return getDependBeanClass(((ParameterizedType) genericSuperclass).getActualTypeArguments()[index]);
            } else {
                return getDependBeanClass(genericType);
            }
        } else {
            return getDependBeanClass(genericType);
        }
    }

    /**
     * 获取依赖的bean对象类型
     * @param genericType 原始类型
     * @return 依赖的bean对象类型
     */
    public static Class<?> getDependBeanClass(Type genericType) {
        Class<?> type;
        ParameterizedType parameterizedType = null;
        if (genericType instanceof ParameterizedType) {
            parameterizedType = (ParameterizedType) genericType;
            type = (Class<?>) parameterizedType.getRawType();
        } else {
            type = (Class<?>) genericType;
        }

        if (type.isAssignableFrom(Collection.class) && parameterizedType != null) {
            return getParameterizedTypeFirstArgumentClass(parameterizedType);
        }
        if (type.isAssignableFrom(List.class) && parameterizedType != null) {
            return getParameterizedTypeFirstArgumentClass(parameterizedType);
        }
        if (type.isAssignableFrom(Set.class) && parameterizedType != null) {
            return getParameterizedTypeFirstArgumentClass(parameterizedType);
        }
        return type;
    }

    /**
     * 判断依赖的bean是否是{@link Collection} 集合对象
     * @param genericType 原始类型
     * @return 依赖的bean是否是{@link Collection} 集合对象
     */
    public static boolean isCollectionDependBean(Type genericType) {
        return Collection.class.isAssignableFrom(getDependBeanType(genericType));
    }

    /**
     * 判断依赖的bean是否是{@link Collection} 集合对象
     * @param genericType 原始类型
     * @return 依赖的bean是否是{@link Collection} 集合对象
     */
    public static boolean isCollectionOrArrayDependBean(Type genericType) {
        return isCollectionDependBean(genericType) || isArrayDependBean(genericType);
    }

    /**
     * 判断依赖的bean是否是数组对象
     * @param genericType 原始类型
     * @return 依赖的bean是否是数组对象
     */
    public static boolean isArrayDependBean(Type genericType) {
        Class<?> clazz = getDependBeanType(genericType);
        if (!clazz.isArray()) {
            return false;
        }
        Class<?> componentType = clazz.getComponentType();
        return componentType != null;
    }

    /**
     * 获取数组的元素类型
     * @param genericType 原始类型
     * @return 数组的元素类型
     */
    public static Class<?> getArrayComponentType(Type genericType) {
        Class<?> clazz = getDependBeanType(genericType);
        if (!clazz.isArray()) {
            return null;
        }
        return clazz.getComponentType();
    }

    /**
     * 获取依赖的bean的真正类型
     * @param genericType 原始类型
     * @return 依赖的bean的真正类型
     */
    public static Class<?> getDependBeanType(Type genericType) {
        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            return (Class<?>) parameterizedType.getRawType();
        }
        return  (Class<?>) genericType;
    }

    /**
     * 获取依赖的{@link Collection} 集合对象大小
     * @param bean 依赖的bean对象
     * @return 依赖的{@link Collection} 集合对象大小，
     * 如果对象为<code>null</code>或者对象不是集合则返回<code>-1</code>
     */
    public static int getDependCollectionBeanSize(Object bean) {
        if (bean != null && Collection.class.isAssignableFrom(bean.getClass())) {
            return ((Collection<?>) bean).size();
        }
        return -1;
    }

    /**
     * 获取给定类的指定注解依赖栈（反转栈对象）
     * @param clazz 给定的类对象
     * @param annotationType 要查找的注解类对象
     * @return 指定注解的依赖栈（反转栈对象），如果找不到指定的注解就返回空栈对象
     */
    public static Stack<Class<? extends Annotation>> findReverseSpecifiedAnnotationStack(Class<?> clazz, Class<? extends Annotation> annotationType) {
        Stack<Class<? extends Annotation>> stack = findSpecifiedAnnotationStack(clazz, annotationType);
        if (stack.isEmpty()) {
            return stack;
        }
        Stack<Class<? extends Annotation>> reverseStack = new Stack<>();
        while (!stack.isEmpty()) {
            reverseStack.push(stack.pop());
        }
        return reverseStack;
    }

    /**
     * 获取给定类的指定注解依赖栈
     * @param clazz 给定的类对象
     * @param annotationType 要查找的注解类对象
     * @return 指定注解的依赖栈，如果找不到指定的注解就返回空栈对象
     */
    public static Stack<Class<? extends Annotation>> findSpecifiedAnnotationStack(Class<?> clazz, Class<? extends Annotation> annotationType) {
        for (Annotation annotation : clazz.getAnnotations()) {
            Stack<Class<? extends Annotation>> stack = new Stack<>();
            Class<? extends Annotation> type = annotation.annotationType();
            stack.push(type);
            doFindSpecifiedAnnotationStack(type, annotationType, stack);

            if (stack.peek().equals(annotationType)) {
                return stack;
            }
        }
        return new Stack<>();
    }

    /**
     * 获取给定类的指定注解所有依赖栈（反转栈对象）
     * @param clazz 给定的类对象
     * @param annotationType 要查找的注解类对象
     * @return 指定注解的依赖栈（反转栈对象），如果找不到指定的注解就返回空栈对象
     */
    public static List<Stack<Class<? extends Annotation>>> findReverseAllSpecifiedAnnotationStack(Class<?> clazz, Class<? extends Annotation> annotationType) {
        List<Stack<Class<? extends Annotation>>> stacks = findAllSpecifiedAnnotationStack(clazz, annotationType);
        if (stacks.isEmpty()) {
            return stacks;
        }
        List<Stack<Class<? extends Annotation>>> reverseStacks = new ArrayList<>(stacks.size());
        for (Stack<Class<? extends Annotation>> stack : stacks) {
            Stack<Class<? extends Annotation>> reverseStack = new Stack<>();
            while (!stack.isEmpty()) {
                reverseStack.push(stack.pop());
            }
            reverseStacks.add(reverseStack);
        }
        return reverseStacks;
    }

    /**
     * 获取给定类的指定注解所有依赖栈
     * @param clazz 给定的类对象
     * @param annotationType 要查找的注解类对象
     * @return 指定注解的依赖栈，如果找不到指定的注解就返回空栈对象
     */
    public static List<Stack<Class<? extends Annotation>>> findAllSpecifiedAnnotationStack(Class<?> clazz, Class<? extends Annotation> annotationType) {
        List<Stack<Class<? extends Annotation>>> stacks = new ArrayList<>();
        for (Annotation annotation : clazz.getAnnotations()) {
            Stack<Class<? extends Annotation>> stack = new Stack<>();
            Class<? extends Annotation> type = annotation.annotationType();
            stack.push(type);
            doFindSpecifiedAnnotationStack(type, annotationType, stack);

            if (stack.peek().equals(annotationType)) {
                stacks.add(stack);
            }
        }
        return stacks;
    }

    /**
     * 给定的类是否存在指定的注解
     * @param clazz 给定的类
     * @param annotationType 注解类对象
     * @return 是否存在指定的注解
     */
    public static boolean isExistSpecifiedAnnotation(Class<?> clazz, Class<? extends Annotation> annotationType) {
        return getAnnotationsOnClass(clazz).contains(annotationType);
    }

    /**
     * 从类对象上获取所有的注解类对象
     * @param clazz 类对象
     * @return 类对象上所有的注解类对象
     */
    public static Set<Class<? extends Annotation>> getAnnotationsOnClass(Class<?> clazz) {
        Set<Class<? extends Annotation>> cacheAnnotationTypes = new HashSet<>();
        doGetAnnotationsOnClass(clazz, cacheAnnotationTypes);
        return cacheAnnotationTypes;
    }

    /**
     * 获取声明的接口类
     * @param beanClass bean类型
     * @return 声名的接口类
     */
    public static Class<?>[] getDeclaredInterfaces(Class<?> beanClass) {
        List<Class<?>> classes = new LinkedList<>();
        Class<?> currentClass = beanClass;
        do {
            appendMatchInterfaceClass(currentClass, classes);
            currentClass = currentClass.getSuperclass();
        } while (currentClass != null);
        return classes.toArray(new Class[0]);
    }

    /**
     * 获取类声明元数据
     * @param beanClass bean类型
     * @return 类声明元数据
     */
    public static ClassDeclaredMetadata getDeclaredMetadata(Class<?> beanClass) {
        List<Class<?>> classes = new LinkedList<>();
        List<Class<?>> interfaces = new LinkedList<>();
        Class<?> currentClass = beanClass;
        do {
            appendMatchInterfaceClass(currentClass, interfaces);
            currentClass = currentClass.getSuperclass();
            if (!Object.class.equals(currentClass)) {
                classes.add(currentClass);
            }
        } while (currentClass != null && !Object.class.equals(currentClass));
        ClassDeclaredMetadata classDeclaredMetadata = new ClassDeclaredMetadata();
        classDeclaredMetadata.setDeclaredClasses(classes.toArray(new Class[0]));
        classDeclaredMetadata.setDeclaredInterfaces(interfaces.toArray(new Class[0]));
        return classDeclaredMetadata;
    }

    /**
     * 执行获取给定类的指定注解依赖栈
     * @param annotationClazz 注解类对象
     * @param annotationType 要查找的注解类对象
     * @param stack 栈对象
     */
    private static void doFindSpecifiedAnnotationStack(Class<? extends Annotation> annotationClazz, Class<? extends Annotation> annotationType,
                                                       Stack<Class<? extends Annotation>> stack) {
        for (Annotation annotation : annotationClazz.getAnnotations()) {
            Class<? extends Annotation> type = annotation.annotationType();
            if (type.equals(annotationType)) {
                stack.push(annotationType);
                return;
            } else if (stack.peek().equals(type)) {
                stack.pop();
                return;
            }
            stack.push(type);
            doFindSpecifiedAnnotationStack(type, annotationType, stack);
        }
    }

    /**
     * 追加匹配的接口类
     * @param interfaceClass bean类型
     * @param classes 保存匹配的接口类集合
     */
    private static void appendMatchInterfaceClass(Class<?> interfaceClass, List<Class<?>> classes) {
        for (Class<?> innerInterfaceClass : interfaceClass.getInterfaces()) {
            if (innerInterfaceClass.getPackage() != null
                    && Arrays.stream(IGNORE_PACKAGES).noneMatch(p -> innerInterfaceClass.getPackage().getName().startsWith(p))
                    && !innerInterfaceClass.getName().contains(INNER_CLASS_SYMBOL)) {
                classes.add(innerInterfaceClass);
                appendMatchInterfaceClass(innerInterfaceClass, classes);
            }
        }
    }

    /**
     * 执行从类对象上获取所有的注解类对象
     * @param clazz 类对象
     * @param cacheAnnotationTypes 注解缓存集合
     */
    private static void doGetAnnotationsOnClass(Class<?> clazz, Set<Class<? extends Annotation>> cacheAnnotationTypes) {
        for (Annotation annotation : clazz.getAnnotations()) {
            Class<? extends Annotation> type = annotation.annotationType();
            if (cacheAnnotationTypes.contains(type)) {
                continue;
            }
            cacheAnnotationTypes.add(type);
            doGetAnnotationsOnClass(type, cacheAnnotationTypes);
        }
    }

    /**
     * 获取参数化类型的第一个参数类型
     * @param parameterizedType 参数化类型
     * @return 参数化类型的第一个参数类型
     */
    private static Class<?> getParameterizedTypeFirstArgumentClass(ParameterizedType parameterizedType) {
        Type type = parameterizedType.getActualTypeArguments()[0];
        if (type instanceof Class) {
            return (Class<?>) type;
        } else if (type instanceof ParameterizedType) {
            return (Class<?>) ((ParameterizedType) type).getRawType();
        } else {
            throw new BeanException("The Generic Class Info Not Found.");
        }
    }

    /**
     * 判断是否是cglib的代理类
     * @param clazz 要判断的代理类
     * @return 是否是cglib的代理类
     */
    private static boolean isCglibProxyObject(Class<?> clazz) {
        return clazz.getName().contains(CGLIB_CLASS_SYMBOL);
    }

    /**
     * 类声明元数据
     * @author sprout
     * 2023-07-27 09:22
     */
    public static class ClassDeclaredMetadata {

        /** 声明的接口数组 */
        private Class<?>[] declaredInterfaces;

        /** 声明的超类数组 */
        private Class<?>[] declaredClasses;

        /**
         * 获取声明的接口数组
         * @return 声明的接口数组
         */
        public Class<?>[] getDeclaredInterfaces() {
            return declaredInterfaces;
        }

        /**
         * 设置声明的接口数组
         * @param declaredInterfaces 声明的接口数组
         */
        public void setDeclaredInterfaces(Class<?>[] declaredInterfaces) {
            this.declaredInterfaces = declaredInterfaces;
        }

        /**
         * 获取声明的接口数组
         * @return 声明的接口数组
         */
        public Class<?>[] getDeclaredClasses() {
            return declaredClasses;
        }

        /**
         * 设置声明的接口数组
         * @param declaredClasses 声明的接口数组
         */
        public void setDeclaredClasses(Class<?>[] declaredClasses) {
            this.declaredClasses = declaredClasses;
        }
    }
}
